class Component{
    constructor(name, width, height, x, y, connections){
        this.name = name;
        this.width = width;
        this.height = height;
        this.connections = [];
        this.connections = this.connections.length == 0 ? [] : connections;
        this.x = x;
        this.y = y;
        this.locked = false; //元器件是否锁定坐标
    }

    add_connection(component){
        this.connections.push(component);
    }
}





class PCBPlacer{
    constructor(width, height){
        this.pcb_width = width;
        this.pcb_height = height;
        this.components = [];
        this.attraction_coeff = 0.01;   //吸引力系数
        this.repulsion_coeff = 10000;    //斥力系数
        this.edge_repulsion = 100;       //边缘斥力系数
        this.damping = 0.85;             //阻尼系数
        this.max_speed = 2.0;            //最大移动速度
    }

        
    add_component(component){
        this.components.push(component);
    }
        
    calculate_forces(){
        var forces = new Array(this.components.length).fill(0).map(() => new Array(2).fill(0));
        
        for(var ic = 0; ic < this.components.length; ic++){
            for(var io = 0; io < this.components[ic].connections.length; io++){

                var j_idx = -1;
                for(var j = 0;j < this.components.length; j++){
                    if(this.components[ic].connections[io].name == this.components[j].name){
                        j_idx = j;
                        break;
                    }
                }

                dx = this.components[j_idx].x - this.components[ic].x;
                dy = this.components[j_idx].y - this.components[ic].y;
                distance = Math.hypot(dx, dy); //计算dx，dy为直角边的三角形的斜边长度

                if(distance > 0){
                    force = this.attraction_coeff * distance;
                    forces[ic][0] += force * dx / distance;
                    forces[ic][1] += force * dy / distance;
                }
            }
        }

        for(var i = 0; i < this.components.length; i++){
            for(var j = i + 1; j < this.components.length; j++){
                var comp1 = this.components[i];
                var comp2 = this.components[j];
                
                var dx = comp2.x - comp1.x;
                var dy = comp2.y - comp1.y;
                var distance = Math.hypot(dx, dy);

                var min_distance = (comp1.width + comp2.width)/2 + 5;
                if(distance < min_distance){
                    force = this.repulsion_coeff / Math.max(distance, 0.1) * Math.max(distance, 0.1);
                    forces[i][0] -= force * dx / distance;
                    forces[i][1] -= force * dy / distance;
                    forces[j][0] += force * dx / distance;
                    forces[j][1] += force * dy / distance;
                }
            }
        }


        for(var i = 0; i < this.components.length; i++){
            var comp = this.components[i];
            //左边界
            var force = this.edge_repulsion / (comp.x + 1);
            forces[i][0] += force;
            // 右边界
            force = this.edge_repulsion / (this.pcb_width - comp.x + 1);
            forces[i][0] -= force;
            // 下边界
            force = this.edge_repulsion / (comp.y + 1);
            forces[i][1] += force;
            // 上边界
            force = this.edge_repulsion / (this.pcb_height - comp.y + 1);
            forces[i][1] -= force;
        }

        return forces;
    }
    
    

    update_positions(forces){
        for (var i = 0; i < this.components.length; i++) {
            if (this.components[i].locked == true){
                continue;
            }

            //限制最大速度
            var fx = Math.max(Math.min(forces[i][0], this.max_speed), -this.max_speed);
            var fy = Math.max(Math.min(forces[i][1], this.max_speed), -this.max_speed);
            
            this.components[i].x += fx * this.damping;
            this.components[i].y += fy * this.damping;
            
            // 保持元件在PCB边界内
            this.components[i].x = Math.max(this.components[i].width/2, Math.min(this.pcb_width - this.components[i].width/2, this.components[i].x));
            this.components[i].y = Math.max(this.components[i].height/2, Math.min(this.pcb_height - this.components[i].height/2, this.components[i].y));
        }    
    }




    visualize(){
        
        var canvas = document.getElementById('canvas');
        var ctx = canvas.getContext("2d");
        var rect = canvas.getBoundingClientRect();
        var w = rect.width;
        var h = rect.height;
        ctx.clearRect(0,0, w, h);
        
        // 绘制元件
        for (var i = 0; i < this.components.length; i++) {
            var comp = this.components[i];
            ctx.fillRect(comp.x+comp.width/2, comp.y+comp.height/2, comp.width, comp.height);


        
            // for(var j =0; j < comp.connections.length; j++){
            //     var co = comp.connections[j];
            //     ctx.moveTo(comp.x, comp.y);
            //     ctx.lineTo(co.x, co.y);
            //     ctx.stroke();
            // }
        }
    }   
}




function getRandomInt(min, max) {
    return Math.floor(Math.random() * (max - min)) + min;
}

function sleep(ms) {
    return new Promise(resolve => {
        setTimeout(resolve, ms);
    });
}





  




function main(){

    // 创建元件
    var comp1 = new Component("U1", getRandomInt(10, 20), getRandomInt(10, 20), getRandomInt(0,200), getRandomInt(0,50), []);
    var comp2 = new Component("U2", getRandomInt(10, 20), getRandomInt(10, 20), getRandomInt(0,200), getRandomInt(0,50), []);
    var comp3 = new Component("U3", getRandomInt(10, 20), getRandomInt(10, 20), getRandomInt(0,200), getRandomInt(0,50), []);
    var comp4 = new Component("U4", getRandomInt(10, 20), getRandomInt(10, 20), getRandomInt(0,200), getRandomInt(0,50), []);
    var comp5 = new Component("U5", getRandomInt(10, 20), getRandomInt(10, 20), getRandomInt(0,200), getRandomInt(0,50), []);
    var comp6 = new Component("U6", getRandomInt(10, 20), getRandomInt(10, 20), getRandomInt(0,200), getRandomInt(0,50), []);
    var comp7 = new Component("U7", getRandomInt(10, 20), getRandomInt(10, 20), getRandomInt(0,200), getRandomInt(0,50), []);
    var comp8 = new Component("U8", getRandomInt(10, 20), getRandomInt(10, 20), getRandomInt(0,200), getRandomInt(0,50), []);
    // comp1.locked = true;

    //添加连接关系
    comp1.add_connection(comp2);
    comp1.add_connection(comp3);
    comp2.add_connection(comp3);
    comp2.add_connection(comp4);
    comp3.add_connection(comp4);
    comp4.add_connection(comp2);
    comp5.add_connection(comp1);
    comp6.add_connection(comp7);
    comp7.add_connection(comp5);
    comp8.add_connection(comp4);
    
    // 创建布局器
    var placer = new PCBPlacer(200, 50);
    placer.add_component(comp1);
    placer.add_component(comp2);
    placer.add_component(comp3);
    placer.add_component(comp4);
    placer.add_component(comp5);
    placer.add_component(comp6);
    placer.add_component(comp7);
    placer.add_component(comp8);
    
    placer.visualize();

    for(var i = 0; i < 100000; i++){
        sleep(3000).then(() => {
            forces = placer.calculate_forces();
            placer.update_positions(forces);
            placer.visualize();
        });
    }
}


main();
